#!/usr/bin/env python

"""Discover available WSN devices.

Copyright (C) Sarah Mount, 2008.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

Functions to discover available WSN devices via USB, Bluetooth, etc. 
Current known device classes are:
 * Lego NXT bricks.
 * TinyOS devices (MICA series, Telos / Tmote series, etc).
 * Dingo managed simulated networks.
 * Dingo managed Gumstix networks.
 * Dingo mixed-mode networks.

"""

__author__ = 'Sarah Mount <s.mount@wlv.ac.uk>'
__date__   = 'March 2008'

import copy, glob, os, string, sys

hasNXT    = False
"""
@var: Whether or not the Python nxt libraries are installed.
@type: bool
"""
hasTinyOS = False
"""
@var: Whether or not a TinyOS installation is available on disk.
@type: bool
"""

ID_VENDOR_TINYOS = '0403'
"""
@var: Vendor ID for TinyOS devices. Used by procfs for USB handshaking.
@type: str
"""
ID_PRODUCT_TINYOS = '6001'
"""
@var: Product ID for TinyOS devices. Used by procfs for USB handshaking.
@type: str
"""

try:
    import nxt
    import nxt.locator
    hasNXT = True
except:
    print 'No support for Lego NXT hardware.'

motelist = 'motelist -c' # by Cory Sharp and Joe Polastre, from TinyOS-1.x

s = os.popen('which ' + motelist)
if not s.readlines():
    print 'No support for TinyOS based hardware.'
else:
    hasTinyOS = True


def snarf(filename):
    """cat a file and return it's contents.
    Returns empty string if file not found.

    @type filename: str
    @param filename: Path to file on disk that we need to snarf.
    @rtype: str
    """
    s = ''
    try:
    	fd = open(filename)
    	s = fd.read()
    	fd.close()
    except IOError:
        pass
    return s.strip()


def discoverTinyOS():
    """Discover TinyOS class devices, such as the Tmote Sky or Crossbow MICAZ.
    Works by scanning sysfs (rather than procfs) for FTDI devices.
    Will not work on 2.4 kernels where procfs should be used.
    Based on the GPL'd "motelist" Perl script by Cory Sharp and 
    Joe Polastre from the TinyOS-1.x installation.

    @rtype: list
    """
    if not hasTinyOS:
        return []
    syspath = '/sys/bus/usb/drivers/usb/'
    drivers = glob.glob(syspath+'/*')
    ftdidevs = {}
    keys = {'serial':'', 'product':'', 'manufacturer':''}
    # Find FTDI devices in sysfs
    for path in drivers:
        if (snarf(path+'/idVendor') == ID_VENDOR_TINYOS and
            snarf(path+'/idProduct') == ID_PRODUCT_TINYOS):
            fname = path.lstrip(syspath) # get filename and strip path
            ftdidevs[fname] = copy.copy(keys)
    # Grab device info
    for dev in ftdidevs.keys():
        for info in keys:
            ftdidevs[dev][info] = snarf(syspath+dev+'/'+info)
    # Find tty device
    for dev in ftdidevs.keys():
        ttyglob = glob.glob(syspath+dev+'/'+dev+':1.0/tty*')
        tty = (ttyglob[0]).lstrip(syspath+dev+'/'+dev+':1.0/')
        ftdidevs[dev]['device'] = '/dev/' + tty
    # Marshall results
    results = [] # (reference, device, description)
    for dev in ftdidevs.keys():
        serial = ftdidevs[dev]['serial']
        device = ftdidevs[dev]['device']
        desc = ('\t' + ftdidevs[dev]['manufacturer'] + ' ' +
                string.capwords(ftdidevs[dev]['product']))
        results.append((serial, device, desc))
    return results


def discoverNXT():
    """Discover Lego NXT bricks connected via Bluetooth or USB.

    @rtype: list
    """
    if not hasNXT:
        return []
    results = [] # (reference, device, description)
    desc = 'Lego NXT brick'
    gen = nxt.locator.find_bricks()
    try:
        sock = gen.next()
        brick = sock.connect()
        name, host, signal_strength, user_flash = brick.get_device_info()
        sock.close()
        name = name.strip()
        results.append((name, host, copy.copy(desc)))
    except StopIteration:
        pass
    return results


def discoverDingo():
    """Detect simulated, live or mixed-mode Dingo networks.
    Feature not implemented.

    @rtype: list
    """
    print 'Beta release cannot discover Dingo networks. Sorry.'
    results = [] # (reference, device, description)
    return results


